#include "RecordSystem.h"

using namespace std;
/*-----------------------------------------------------------------------------------------
--
--	FUNCTION: RecordSystem::RecordSystem()
--
--	Date: March 25, 2010
--
--	REVISIONS (Date and Description):
--
--	DESIGNER: John Kuo
--
--	PROGRAMMER: Brad Paugh
--
--  INTERFACE: RecordSystem::RecordSystem()
--
--
--  RETURNS: N/A
--
--	NOTES: Sets the device format for recording, opens the device and initializes variables
--
-----------------------------------------------------------------------------------------*/
RecordSystem::RecordSystem()
{
	WAVEINCAPS wic;
	waveInGetDevCaps(WAVE_MAPPER, &wic, sizeof(wic));

	//checking if the device supports this format
	if (wic.dwFormats & WAVE_FORMAT_4S16)
    {
		format.nChannels      = 2;
		format.nSamplesPerSec = 44100;
    }
	else
	{
		format.nChannels = wic.wChannels;
		format.nSamplesPerSec  = 22050;
	}

	format.wFormatTag = WAVE_FORMAT_PCM;
	format.wBitsPerSample = 8;
	format.nBlockAlign = format.nChannels*format.wBitsPerSample/8;
	format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign;
	format.cbSize = 0;

	
	bufferFullEvent = CreateEvent(NULL, TRUE, FALSE, TEXT("bufferevent"));
	canRead = CreateEvent(NULL, TRUE, FALSE, TEXT("canreadevent"));

	MMRESULT mr;
    if ((mr = waveInOpen(&inputDevice, WAVE_MAPPER,&format, (DWORD)bufferFullEvent, 0, CALLBACK_EVENT)) != MMSYSERR_NOERROR)
	{
		char txt[1024];
		waveInGetErrorText(mr, (WCHAR*)txt, 1024);
		printf("%s\n", txt);
	}

	currentBuffer = 0;
	bufferToGet = 0;
	threadStop = false;
    bufferData = (char**)malloc(sizeof(char*));
    headers = (WAVEHDR*)malloc(sizeof(WAVEHDR));
}

/*-----------------------------------------------------------------------------------------
--
--	FUNCTION: RecordSystem::startRecording()
--
--	Date: March 25, 2010
--
--	REVISIONS (Date and Description):
--
--	DESIGNER: John Kuo
--
--	PROGRAMMER: Brad Paugh
--
--  INTERFACE: RecordSystem::startRecording()
--
--
--  RETURNS: void
--
--	NOTES: starts recording on the device
--
-----------------------------------------------------------------------------------------*/
void RecordSystem::startRecording()
{
	MMRESULT mr;
	if((mr = waveInStart(inputDevice)) != MMSYSERR_NOERROR)
	{
		char txt[1024];
		waveInGetErrorText(mr, (WCHAR*)txt, 1024);
		printf("%s\n", txt);
	}

	CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)waveInProc, this, 0, NULL);
}

/*-----------------------------------------------------------------------------------------
--
--	FUNCTION: RecordSystem::makeHeader()
--
--	Date: March 25, 2010
--
--	REVISIONS (Date and Description):
--
--	DESIGNER: John Kuo
--
--	PROGRAMMER: Brad Paugh
--
--  INTERFACE: RecordSystem::makeHeader(char* headerData)
--				char* headerData - buffer to store header in
--
--
--  RETURNS: void
--
--	NOTES: makes a header to be added to the front of the packet to be sent across the network
--
-----------------------------------------------------------------------------------------*/
void RecordSystem::makeHeader(char* headerData)
{
    int bytesWritten = sprintf(headerData, "%d|%d|%lu|%d|%d|%lu|", format.wFormatTag, format.nChannels, format.nSamplesPerSec, format.wBitsPerSample, format.nBlockAlign, format.nAvgBytesPerSec);
	for (int i = bytesWritten; i < 23; i++)
	{
		headerData[i] = '|';
	}
    headerData[23] = '\0';
}

/*-----------------------------------------------------------------------------------------
--
--	FUNCTION: RecordSystem::getNextBuffer(char* buffer)
--
--	Date: March 25, 2010
--
--	REVISIONS (Date and Description):
--
--	DESIGNER: John Kuo
--
--	PROGRAMMER: Brad Paugh
--
--  INTERFACE: RecordSystem::RecordSystem()
--
--
--  RETURNS: void
--
--	NOTES: gets the next audio buffer that has been read from the device. Will never read
--	a buffer that is currently being read in
--
-----------------------------------------------------------------------------------------*/
void RecordSystem::getNextBuffer(char* buffer)
{
	//wait until the buffer is ready if it is not yet
	WaitForSingleObject(canRead, INFINITE);
	ResetEvent(canRead);
	char* header = (char*)malloc(24);
	makeHeader(header);
	memcpy(buffer, header, HEADERSIZE);
	memcpy(&buffer[HEADERSIZE], headers[bufferToGet].lpData, BUFSIZE-HEADERSIZE);
	bufferToGet++;
}

/*-----------------------------------------------------------------------------------------
--
--	FUNCTION: RecordSystem::getMsPerPacket(int packetSize)
--
--	Date: March 25, 2010
--
--	REVISIONS (Date and Description):
--
--	DESIGNER: John Kuo
--
--	PROGRAMMER: Brad Paugh
--
--  INTERFACE: RecordSystem::getMsPerPacket(int packetSize)
--
--
--  RETURNS: int - millisecs per packet
--
--	NOTES: returns the number of milliseconds in a packet
--
-----------------------------------------------------------------------------------------*/
int RecordSystem::getMsPerPacket(int packetSize)
{
	int millisecs;
	millisecs = packetSize/(format.nAvgBytesPerSec/1000); //convert to avg bytes per millisec
	return millisecs;
}

/*-----------------------------------------------------------------------------------------
--
--	FUNCTION: RecordSystem::close()
--
--	Date: March 25, 2010
--
--	REVISIONS (Date and Description):
--
--	DESIGNER: John Kuo
--
--	PROGRAMMER: Brad Paugh
--
--  INTERFACE: RecordSystem::close()
--
--
--  RETURNS: void
--
--	NOTES: stops recording and closes the device
--
-----------------------------------------------------------------------------------------*/
void RecordSystem::close()
{
	threadStop = true;
	SetEvent(bufferFullEvent);
	currentBuffer = 0;
	bufferToGet = 0;
	waveInReset(inputDevice);
	waveInClose(inputDevice);
}

/*-----------------------------------------------------------------------------------------
--
--	FUNCTION: RecordSystem::waveInProc(void* param)
--
--	Date: March 25, 2010
--
--	REVISIONS (Date and Description):
--
--	DESIGNER: John Kuo
--
--	PROGRAMMER: Brad Paugh
--
--  INTERFACE: RecordSystem::close()
--
--
--  RETURNS: void
--
--	NOTES: This is the the static method to start the thread which handles buffering
--
-----------------------------------------------------------------------------------------*/
void RecordSystem::waveInProc(void* param)
{
	((RecordSystem*)param)->waveInProcInstance();
}

/*-----------------------------------------------------------------------------------------
--
--	FUNCTION: RecordSystem::waveInProcInstance()
--
--	Date: March 25, 2010
--
--	REVISIONS (Date and Description):
--
--	DESIGNER: John Kuo
--
--	PROGRAMMER: Brad Paugh
--
--  INTERFACE: RecordSystem::waveInProcInstance()
--
--  RETURNS: void
--
--	NOTES: the method that handles all the buffering. Works like a callback function except
--		using events as we do not have a wndproc. the event will fire whenever a buffer is filled
--		with audio data. Buffer is dynamically allocated and will keep growing until recording is stopped.
--
-----------------------------------------------------------------------------------------*/
void RecordSystem::waveInProcInstance()
{
	while(1)
	{
		MMRESULT mr;
		//dynamically allocated array which holds buffer data
		if ((bufferData = (char**)realloc(bufferData, (sizeof(char*)*currentBuffer)+sizeof(char*))) == 0)
		{
			printf("REALLOC ERROR");
		}
		bufferData[currentBuffer] = (char *)malloc(BUFSIZE-HEADERSIZE);

		//array of header which is dynamically allocated with each iteration
		headers = (WAVEHDR*)realloc(headers, (sizeof(WAVEHDR)*currentBuffer)+sizeof(WAVEHDR));
		headers[currentBuffer].lpData = bufferData[currentBuffer];
		headers[currentBuffer].dwBufferLength = BUFSIZE-HEADERSIZE;
		headers[currentBuffer].dwBytesRecorded =  0;
		headers[currentBuffer].dwUser = 0;
		headers[currentBuffer].dwFlags = 0;
		headers[currentBuffer].dwLoops = 0;

		if ((mr = waveInPrepareHeader(inputDevice,&headers[currentBuffer],sizeof(headers[currentBuffer]))) != MMSYSERR_NOERROR)
		{
			char txt[1024];
			waveInGetErrorText(mr, (WCHAR*)txt, 1024);
			printf("%s\n", txt);
		}
		if ((mr = waveInAddBuffer(inputDevice, &headers[currentBuffer], sizeof(headers[currentBuffer]))) != MMSYSERR_NOERROR)
		{
			char txt[1024];
			waveInGetErrorText(mr, (WCHAR*)txt, 1024);
			printf("%s\n", txt);
		}
		//event will be signalled when a buffer is full.
		WaitForSingleObject(bufferFullEvent, INFINITE);
		ResetEvent(bufferFullEvent);
		if (threadStop)
			break;
		currentBuffer++;
		//we signal that it is safe to read
		if (currentBuffer > bufferToGet)
			SetEvent(canRead);
	}
}
